package com.tensquare.gateway.filters;

import com.alibaba.fastjson.JSONObject;
import com.tensquare.constants.TokenConstants;
import com.tensquare.entity.JWTInfo;
import com.tensquare.entity.Result;
import com.tensquare.entity.StatusCode;
import com.tensquare.exception.UserTokenException;
import com.tensquare.gateway.service.PermissionService;
import com.tensquare.gateway.service.RsaService;
import com.tensquare.rsa.RsaKeys;
import com.tensquare.util.JwtTokenUtil;
import com.tensquare.util.StringHelper;
import io.jsonwebtoken.Claims;
import io.netty.buffer.ByteBufAllocator;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.*;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.validation.constraints.NotNull;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

@Configuration
@Slf4j
public class RsaRequestFilter implements GlobalFilter, Ordered {

    @Autowired
    private RsaService rsaService;
    @Autowired
    private PermissionService permissionService;

    private static final String startWith = "/auth/captcha,/auth/login,/api/admin/user/reg";

    private static final boolean IS_USE_ENC = true;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("---gateway---");
        ServerHttpRequest request = exchange.getRequest();
        request.mutate().header("IS_AFTER_GATEWAY", "Y");
        MediaType contentType = request.getHeaders().getContentType();
        String value = request.getPath().pathWithinApplication().value();
        log.info("path->{}", value);
        if (!(contentType != null && request.getMethod() == HttpMethod.POST && contentType.isCompatibleWith(MediaType.APPLICATION_JSON_UTF8))) {
            log.info("path->{}", value);
        }
        if (checkUrl(value)) {
            return chain.filter(exchange);
        }
        List<String> authentication = request.getHeaders().get(TokenConstants.TOKEN_VALUE);
        String authToken = null;
        if (null != authentication) {
            authToken = authentication.get(0);
        }
        Mono<Void> e = checkJWTInfo(exchange, authToken);
        if (e != null) return e;

//        if (!checkPermission(jwtInfo, value)) {
//            return getVoidMono(exchange,new Result(false,"无权限",StatusCode.REPERROR),HttpStatus.UNAUTHORIZED);
//        }

//        if (true) {
//            return getRequestParams(exchange, chain);
//        }

        if (!IS_USE_ENC) {
            return chain.filter(exchange);
        }
        if (contentType != null && request.getMethod() == HttpMethod.POST && contentType.isCompatibleWith(MediaType.APPLICATION_JSON_UTF8)) {
            return decRequestParam(exchange, chain);
        }
        return chain.filter(exchange);
    }

    /**
     * 对请求参数进行验证是否合法
     * 在其他微服务使用POST方法，并且使用了@Requestbody时，但是前端没有传body，
     * 就会出现没有返回的情况！！！！！！！！！！！（HttpMessageNotReadableException
     */
    private Mono<Void> decRequestParam(ServerWebExchange exchange, GatewayFilterChain chain) {
        return DataBufferUtils.join(exchange.getRequest().getBody())
                .flatMap(buffer -> {

//                    String decParams = getPostParams(buffer);

                    byte[] bytes = new byte[buffer.readableByteCount()];
                    buffer.read(bytes);

                    //这里的s就是前端post请求发过来的参数
                    String s = new String(bytes, StandardCharsets.UTF_8);
                    String value = exchange.getRequest().getPath().pathWithinApplication().value();
                    log.info("path->{}", value);
                    log.info("params->{}", s);
//                    if (StringHelper.isEmpty(decParams) || JSONObject.parseObject(decParams) == null) {
//                        return getVoidMono(exchange, new Result(false, "异常的请求", StatusCode.ERROR), HttpStatus.FORBIDDEN);
//                    }
                    //这个str是就转发到其他微服务的参数
                    DataBuffer bodyDataBuffer = stringBuffer(s);
                    Flux<DataBuffer> cachedFlux = Flux
                            .defer(() -> Flux.just(bodyDataBuffer));
                    ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(
                            exchange.getRequest()) {
                        @Override
                        public Flux<DataBuffer> getBody() {
                            return cachedFlux;
                        }
                    };
//                        exchange.getAttributes().put(ConstantFilter.CACHED_REQUEST_BODY_OBJECT_KEY,decParams);
                    return chain.filter(exchange.mutate().request(mutatedRequest).build());
                });
    }

    /**
     * 这种权限的校验方式与RestFul风格的API有冲突，我解决不了~
     */
    private boolean checkPermission(JWTInfo jwtInfo, String url) {
        return permissionService.checkPermission(jwtInfo.getId(), url);
    }

    /**
     * 验证JWT是否有效
     */
    private Mono<Void> checkJWTInfo(ServerWebExchange exchange, String authToken) {
        try {
            Claims infoFromToken = JwtTokenUtil.getInfoFromToken(authToken, TokenConstants.SECRET_KEY);
            JWTInfo jwtInfo = getJWTInfo(infoFromToken);
            exchange.getRequest().mutate().header("userId", jwtInfo.getId());
        } catch (UserTokenException e) {
            return getVoidMono(exchange, new Result(false, e.getMessage(), StatusCode.USER_TOKEN_EXPIRED), HttpStatus.OK);
        } catch (Exception e) {
            return getVoidMono(exchange, new Result(false, "User Token Error!", StatusCode.USER_TOKEN_ERROR), HttpStatus.OK);
        }
        return null;
    }

    /**
     * 获取用户JWT的信息
     */
    private JWTInfo getJWTInfo(Claims infoFromToken) {
        return new JWTInfo(StringHelper.getObjectValue(infoFromToken.get(TokenConstants.JWT_KEY_USER_ACCOUNT)),
                StringHelper.getObjectValue(infoFromToken.get(TokenConstants.JWT_KEY_NICKNAME)),
                StringHelper.getObjectValue(infoFromToken.get(TokenConstants.JWT_KEY_id)));
//        String s = redisTemplate.opsForValue().get(RedisConstant.REDIS_USER_TOKEN_KEY + ":" + jwtInfo.getId());
//        if (null == s | "".equals(s)) {
//            throw new UserTokenException("User Token Expired");
//        }
        //更新JWT的有效时间
//        long expireTime = infoFromToken.getExpiration().getTime();
//        long currTime = System.currentTimeMillis();

//        redisTemplate.expire(RedisConstant.REDIS_USER_TOKEN_KEY + ":" + jwtInfo.getId(),RedisConstant.REDIS_USER_TOKEN_EXPIRED_TIME, TimeUnit.SECONDS);
    }

    /**
     * 验证是否要拦截URL
     */
    private boolean checkUrl(String url) {
        for (String s : startWith.split(",")) {
//            if (url.startsWith(s)) {
//                return true;
//            }
            if (url.equals(s)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取前端post发送过来的参数,并进行解密
     */
    private String getPostParams(DataBuffer buffer) {
        byte[] bytes = new byte[buffer.readableByteCount()];
        buffer.read(bytes);
        //这里的s就是前端post请求发过来的参数
        String s = new String(bytes, StandardCharsets.UTF_8);
        try {
            return rsaService.RSADecryptDataPEM(s, RsaKeys.getServerPrvKeyPkcs8());
        } catch (Exception e) {
            System.err.println("解密出错：" + e.getMessage());
        }
        return null;
    }


    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }

    private DataBuffer stringBuffer(String value) {
        byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
        NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
        DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
        buffer.write(bytes);
        return buffer;
    }

    /**
     * 网关抛异常
     */
    @NotNull
    private Mono<Void> getVoidMono(ServerWebExchange serverWebExchange, Result result, HttpStatus status) {
        serverWebExchange.getResponse().setStatusCode(status);
        byte[] bytes = com.alibaba.fastjson.JSONObject.toJSONString(result).getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = serverWebExchange.getResponse().bufferFactory().wrap(bytes);
        return serverWebExchange.getResponse().writeWith(Flux.just(buffer));
    }

    private String resolveBodyFromRequest(ServerWebExchange exchange) {
        //获取请求体
        Flux<DataBuffer> body = exchange.getRequest().getBody();
        AtomicReference<String> sb = new AtomicReference<>("");
        body.subscribe(buffer -> {
            byte[] bytes = new byte[buffer.readableByteCount()];
            buffer.read(bytes);
            DataBufferUtils.release(buffer);
            String s = new String(bytes, StandardCharsets.UTF_8);
            System.out.println("RsaRequestFilter.resolveBodyFromRequest1" + sb.toString());
            sb.set(s);
            System.out.println("RsaRequestFilter.resolveBodyFromRequest2" + sb.toString());
        });
        System.out.println("RsaRequestFilter.resolveBodyFromRequest3" + sb.toString());
        return sb.toString().trim();
    }

}
